package com.base.core.framework.mybatis.service.impl;

import java.io.Serializable;
import java.sql.Connection;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;

import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.util.CollectionUtils;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.base.core.framework.mybatis.entity.BaseV1Ext;
import com.base.core.framework.mybatis.service.BaseService;
import com.base.core.framework.mybatis.utils.ScriptDelegate;
import com.base.core.framework.mybatis.utils.SqlWrapperBuilderV2Ext;
import com.gitee.magic.core.annotations.SqlCondition.SqlConditionType;
import com.gitee.magic.core.exception.ApplicationException;
import com.gitee.magic.core.utils.ReflectUtils;
import com.gitee.magic.core.utils.SnowflakeIdWorker;
import com.gitee.magic.framework.base.constant.Config;
import com.gitee.magic.framework.base.utils.MapConvert;
import com.gitee.magic.framework.base.utils.ParamValue;
import com.gitee.magic.framework.head.constants.BaseCode;
import com.gitee.magic.framework.head.exception.BusinessException;
import com.gitee.magic.framework.head.vo.QueryVO;
import com.gitee.magic.jdbc.persistence.EntityFactory;
import com.gitee.magic.jdbc.persistence.EntityFactoryAware;
import com.gitee.magic.jdbc.persistence.EntityObject;
import com.gitee.magic.jdbc.persistence.source.jdbc.sqlplus.toolkit.StringPool;

/**
 * @author start
 * @param <T>
 * @param <M>
 */
public class BaseServiceImpl<M extends BaseMapper<T>,T extends BaseV1Ext,PK extends Serializable> 
	extends ServiceImpl<M,T> implements BaseService<T,PK>,EntityFactoryAware,InitializingBean {
	
	@Autowired
	private SnowflakeIdWorker snowflakeIdWorker;
	
	private EntityFactory entityFactory;
	
	@Value("${start.framework.print.sqltablescript:false}") 
	private boolean printSqlTableScript;

	@Override
	public void setEntityFactory(EntityFactory factory) {
		this.entityFactory=factory;
	}
	
	@Override
	public void afterPropertiesSet() throws Exception {
		if(printSqlTableScript) {
			ScriptDelegate delelte=new ScriptDelegate();
			try(Connection connection= getSqlSessionFactory().openSession().getConnection()){
				delelte.tableAnalysis(connection,getEntityClass());
			}catch (Exception e) {
				throw new ApplicationException(e);
			}
		}
	}
	
	@Override
	public <P> Long saveForUpdate(PK pk, P param) {
		T target=ReflectUtils.newInstance(getEntityClass());
		BeanUtils.copyProperties(param, target);
		if(pk!=null) {
			target.setId((Long)pk);
			getBaseMapper().updateById(target);
		}else {
			target.setId(snowflakeIdWorker.nextId());
			target.setCreatedDate(new Date());
			getBaseMapper().insert(target);
		}
		return target.getId();
	}

	@Override
	public T load(PK pk) {
		T t=getById(pk);
		if(t==null) {
			if(Config.isSystemFlag()) {
				throw new BusinessException(BaseCode.CODE_1017,pk,getEntityClass().getName());
			}else {
				throw new ApplicationException(String.format(BaseCode.CODE_1017,
						pk,getEntityClass().getName()));
			}
		}
		return t;
	}
	
	@Override
	public <R>R get(List<R> collections) {
		return get(collections, true);
	}

	@Override
	public <R>R get(List<R> collections, Boolean flag) {
		if (collections == null) {
			return null;
		}
		if (collections.isEmpty()) {
			return null;
		}
		if (collections.size() > 1 && flag) {
			throw new ApplicationException("Data repetition");
		}
		return collections.get(0);
	}

	@Override
	public <R>R result(Class<R> prototype,Map<String,Object> data){
		return MapConvert.convert(prototype, data);
	}
	
	@Override
	public <R>List<R> result(Class<R> prototype,List<Map<String,Object>> datas){
		List<R> results=new ArrayList<>();
		for(Map<String,Object> data:datas) {
			results.add(result(prototype, data));
		}
		return results;
	}
	
	@Override
	public <R> QueryVO<R> queryVO(Class<R> prototype, List<Map<String, Object>> datas, int totalCount) {
		List<R> results=new ArrayList<>();
		for(Map<String,Object> data:datas) {
			results.add(result(prototype, data));
		}
		return queryVO(results,totalCount);
	}

	@Override
	public <R> QueryVO<R> queryVO(List<R> results, int totalCount) {
		return new QueryVO<>(results,totalCount);
	}
	
	
	/**
	 * fieldName字段需添加要加unique
	 * @param fieldName
	 * @param value
	 * @return
	 */
	public T load(String fieldName,Object value) {
		QueryWrapper<T> wrapper=new QueryWrapper<>();
		wrapper.eq(fieldName, value);
		wrapper.last("limit 1");
		return getOne(wrapper);
	}
	
	/**
	 * fieldName字段需添加要加unique or normal
	 * @param fieldName
	 * @param value
	 * @return
	 */
	public List<T> queryForList(String fieldName,Object value){
		QueryWrapper<T> wrapper=new QueryWrapper<>();
		wrapper.eq(fieldName, value);
		return getBaseMapper().selectList(wrapper);
	}

	@Override
	public <R,P> R load(Class<R> rpro,P param) {
		return load(rpro,MapConvert.convertExt(param));
	}

	@Override
	public <R> R load(Class<R> rpro,List<ParamValue> params) {
		QueryWrapper<T> wrapper=buildWrapper(rpro,params);
		wrapper.last("limit 1");
		return get(queryForMap(rpro, wrapper));
	}
	
	public int set(PK id,String fieldName,Object value) {
		return set(Arrays.asList(id),fieldName,value);
	}
	
	public int set(Collection<PK> ids,String fieldName,Object value) {
		if(ids.isEmpty()) {
			return 0;
		}
		UpdateWrapper<T> wrapper=new UpdateWrapper<>();
        wrapper.in("id", ids).set(fieldName, value);
        return getBaseMapper().update(null, wrapper);
	}
	
	@Override
	public int remove(PK id) {
		return remove(Arrays.asList(id));
	}

	@Override
	public int remove(PK[] ids) {
		return remove(Arrays.asList(ids));
	}
	
	@Override
	public int remove(Collection<PK> ids) {
		if(ids.size()==0) {
			return 0;
		}
		return getBaseMapper().deleteByIds(ids);
	}
	
	public int remove(String fieldName,Object value) {
		QueryWrapper<T> wrapper=new QueryWrapper<>();
		wrapper.eq(fieldName, value);
		return getBaseMapper().delete(wrapper);
	}

	@Override
	public List<T> queryForByIds(PK[] ids){
		return queryForByIds(Arrays.asList(ids));
	}
	
	@Override
	public List<T> queryForByIds(Collection<PK> ids){
		return queryForIn("id",ids);
	}
	
	public <P>List<T> queryForIn(String fieldName,Collection<P> values) {
		if(CollectionUtils.isEmpty(values)) {
			return new ArrayList<>();
		}
		QueryWrapper<T> wrapper=new QueryWrapper<>();
		wrapper.in(fieldName, values);
		return getBaseMapper().selectList(wrapper);
	}

	@Override
	public <R> List<R> queryForByIds(Class<R> rpro, Collection<PK> ids) {
		return queryForIn(rpro,"id",ids);
	}

	public <R,P> List<R> queryForIn(Class<R> rpro, String fieldName,Collection<P> values) {
		if(CollectionUtils.isEmpty(values)) {
			return new ArrayList<>();
		}
		QueryWrapper<T> wrapper=buildWrapper(rpro,new ArrayList<>());
		wrapper.in(fieldName, values);
		return queryForMap(rpro, wrapper);
	}
	
	@Override
	public List<T> queryForAll() {
		return getBaseMapper().selectList(new QueryWrapper<T>());
	}

	@Override
	public <R> List<R> queryForAll(Class<R> rpro) {
		return result(rpro,queryForMapAll());
	}
	
	@Override
	public List<Map<String, Object>> queryForMapAll() {
		return getBaseMapper().selectMaps(new QueryWrapper<>());
	}
	
	@Override
	public <R, P> List<R> queryForMap(Class<R> rpro,P param) {
		return queryForMap(rpro, MapConvert.convertExt(param));
	}

	@Override
	public <R> List<R> queryForMap(Class<R> rpro,Map<String, Object> params) {
		QueryWrapper<T> wrapper=buildQueryWrapper(rpro,params);
		if(wrapper.getExpression().getOrderBy().isEmpty()) {
			wrapper.orderByDesc("id");
		}
		return queryForMap(rpro, wrapper);
	}
	
	@Override
	public <R> List<R> queryForMap(Class<R> rpro,List<ParamValue> params) {
		QueryWrapper<T> wrapper=buildWrapper(rpro,params);
		return queryForMap(rpro, wrapper);
	}

	public <R> List<R> queryForMap(Class<R> rpro, QueryWrapper<T> wrapper) {
		return result(rpro,getBaseMapper().selectMaps(wrapper));
	}

	public <R> QueryWrapper<T> buildQueryWrapper(Class<R> rpro, Map<String, Object> params) {
		return buildQueryWrapper(true,rpro,params);
	}
	
	public <R> QueryWrapper<T> buildQueryWrapper(Boolean idhandle,Class<R> rpro, Map<String, Object> params){
		List<ParamValue> paramList=new ArrayList<>();
		for(String p:params.keySet()) {
			ParamValue pv=new ParamValue();
			pv.setCondition(new SqlConditionType[]{SqlConditionType.EQ});
			pv.setValue(params.get(p));
			pv.setColumnName(p);
			paramList.add(pv);
		}
		EntityObject eObject=entityFactory.getEntityDefinition(getEntityClass());
		return SqlWrapperBuilderV2Ext.buildQueryWrapper(eObject,idhandle,rpro,paramList);
	}
	
	public <R,P> QueryWrapper<T> buildWrapper(Class<R> rpro, P param) {
		return buildWrapper(rpro,MapConvert.convertExt(param));
	}
	
	public <R> QueryWrapper<T> buildWrapper(Class<R> rpro, Collection<ParamValue> params) {
		EntityObject eObject=entityFactory.getEntityDefinition(getEntityClass());
		return SqlWrapperBuilderV2Ext.buildQueryWrapper(eObject,true,rpro,params);
	}

	///////////////////////////QueryVO

	public <R> QueryVO<R> pageWrapper(Class<R> rpro, QueryWrapper<T> wrapper) {
		List<Map<String, Object>> results = getBaseMapper().selectMaps(wrapper);
        if (results.isEmpty()) {
        	return null;
        }
        //重设查询统计,last("limit 1,10");应清空
        wrapper.select("1");
        Integer totalCount = Integer.parseInt(String.valueOf(getBaseMapper().selectCount(wrapper.last(StringPool.EMPTY))));
        return queryVO(rpro, results, totalCount);
    }

	@Override
	public <R, P> QueryVO<R> pageWrapper(Class<R> rpro, P param) {
		Map<String,Object> params=MapConvert.convert(param);
		int pageIndex=Integer.parseInt(String.valueOf(params.get("pageIndex")));
		int pageSize=Integer.parseInt(String.valueOf(params.get("pageSize")));
		int index=(pageIndex - 1) * pageSize;
		return pageWrapper(rpro,MapConvert.convertExt(param),index,pageSize);
	}

	@Override
	public <R, P> QueryVO<R> pageWrapper(Class<R> rpro, P param, int pageIndex, int pageSize) {
		return pageWrapper(rpro,MapConvert.convertExt(param),pageIndex,pageSize);
	}

	@Override
	public <R> QueryVO<R> pageWrapper(Class<R> rpro, List<ParamValue> params, int pageIndex, int pageSize) {
		QueryWrapper<T> wrapper=buildWrapper(rpro,params);
		if(wrapper.getExpression().getOrderBy().isEmpty()) {
			wrapper.orderByDesc("id");
		}
		wrapper.last("limit " + pageIndex+","+pageSize);
		return pageWrapper(rpro, wrapper);
	}
	
	@Override
	public <R>List<T> streamToList(List<R> result,Function<R,PK> resultMapper){
		List<PK> ids = result.stream().map(resultMapper).distinct().collect(Collectors.toList());
		return queryForByIds(ids);
	}

	@Override
	public <R>Map<PK,T> streamToMap(List<R> result,Function<R,PK> resultMapper,Function<T,PK> valMapper){
		List<T> list=streamToList(result,resultMapper);
		return list.stream().collect(Collectors.toMap(valMapper, Function.identity()));
	}
	
	@Override
	public <R,KV> Map<KV, List<T>> streamToMapGroupingBy(List<R> result, Function<R, PK> resultMapper,Function<T, KV> valMapper) {
		List<T> list=streamToList(result,resultMapper);
		return list.stream().collect(Collectors.groupingBy(valMapper));
	}
	
	@Override
	public <R,P>List<P> streamToList(List<R> result,Function<R,PK> resultMapper,Class<P> rpro){
		List<PK> ids = result.stream().map(resultMapper).distinct().collect(Collectors.toList());
		return queryForByIds(rpro,ids);
	}
	
	@Override
	public <R,P>Map<PK,P> streamToMap(List<R> result,Function<R,PK> resultMapper,Class<P> rpro,Function<P,PK> valMapper){
		List<P> list=streamToList(result,resultMapper,rpro);
		return list.stream().collect(Collectors.toMap(valMapper, Function.identity()));
	}

	@Override
	public <R, P,KV> Map<KV, List<P>> streamToMapGroupingBy(List<R> result, Function<R, PK> resultMapper, Class<P> rpro,
			Function<P, KV> valMapper) {
		List<P> list=streamToList(result,resultMapper,rpro);
		return list.stream().collect(Collectors.groupingBy(valMapper));
	}

	public void select1(PK id,String fieldName, Object value, String message) {
		Map<String,Object> params=new HashMap<>(1);
		params.put(fieldName, value);
		select1(id,params,message);
	}
	
	public void select1(PK id,Map<String,Object> params, String message) {
		QueryWrapper<T> wrapper=new QueryWrapper<>();
		wrapper.select("1");
		wrapper.ne(id!=null,"id", id);
		wrapper.allEq(params);
        wrapper.last("limit 1");
        if(getBaseMapper().selectMaps(wrapper).size() > 0) {
			throw new BusinessException(message);
		}
	}

	public Boolean select1(Map<String,Object> params) {
		QueryWrapper<T> wrapper = new QueryWrapper<>();
        wrapper.select("1");
        wrapper.allEq(params);
        wrapper.last("limit 1");
        return getBaseMapper().selectMaps(wrapper).size() > 0;
	}

}
